﻿/****************************************************
	文件：Program.cs
	作者：WithoutUmbrella_Boy
	邮箱: 1026188730@qq.com
	日期：2021/02/12 19:50   	
	功能：抽象工厂模式
*****************************************************/
using System;
#region 抽象工厂模式
/// <summary>
/// 下面以绝味鸭脖连锁店为例子演示下抽象工厂模式
/// 因为每个地方的喜欢的口味不一样，有些地方喜欢辣点的，有些地方喜欢吃不辣点
/// 客户端调用
/// </summary>
class Client
{
    static void Main(string[] args)
    {
        // 南昌工厂制作南昌的鸭脖和鸭架
        AbstractFactory nanChangFactory = new NanChangFactory();
        //两种调用写法
        YaBo nanChangYabo = nanChangFactory.CreateYaBo();
        nanChangYabo.Print();
        YaJia nanChangYajia = nanChangFactory.CreateYaJia();
        nanChangYajia.Print();
        //nanChangFactory.CreateYaBo().Print();
        //nanChangFactory.CreateYaJia().Print();

        // 上海工厂制作上海的鸭脖和鸭架
        AbstractFactory shangHaiFactory = new ShangHaiFactory();
        shangHaiFactory.CreateYaBo().Print();
        shangHaiFactory.CreateYaJia().Print();

        // 湖南工厂制作湖南的鸭脖和鸭架
        AbstractFactory huNanFactory = new HuNanFactory();
        huNanFactory.CreateYaBo().Print();
        huNanFactory.CreateYaJia().Print();

        Console.ReadKey();
    }
}

/// <summary>
/// 抽象工厂类，提供创建两个不同地方的鸭架和鸭脖的接口
/// </summary>
public abstract class AbstractFactory
{
    // 抽象工厂提供创建一系列产品的接口，这里作为例子，只给出了绝味中鸭脖和鸭架的创建接口
    public abstract YaBo CreateYaBo();
    public abstract YaJia CreateYaJia();
}

/// <summary>
/// 南昌绝味工厂负责制作南昌的鸭脖和鸭架
/// </summary>
public class NanChangFactory : AbstractFactory
{
    // 制作南昌鸭脖
    public override YaBo CreateYaBo()
    {
        return new NanChangYaBo();
    }
    // 制作南昌鸭架
    public override YaJia CreateYaJia()
    {
        return new NanChangYaJia();
    }
}

/// <summary>
/// 上海绝味工厂负责制作上海的鸭脖和鸭架
/// </summary>
public class ShangHaiFactory : AbstractFactory
{
    // 制作上海鸭脖
    public override YaBo CreateYaBo()
    {
        return new ShangHaiYaBo();
    }
    // 制作上海鸭架
    public override YaJia CreateYaJia()
    {
        return new ShangHaiYaJia();
    }
}

/// <summary>
/// 鸭脖抽象类，供每个地方的鸭脖类继承
/// </summary>
public abstract class YaBo
{
    /// <summary>
    /// 打印方法，用于输出信息
    /// </summary>
    public abstract void Print();
}

/// <summary>
/// 鸭架抽象类，供每个地方的鸭架类继承
/// </summary>
public abstract class YaJia
{
    /// <summary>
    /// 打印方法，用于输出信息
    /// </summary>
    public abstract void Print();
}

/// <summary>
/// 南昌的鸭脖类，因为江西人喜欢吃辣的，所以南昌的鸭脖稍微会比上海做的辣
/// </summary>
public class NanChangYaBo : YaBo
{
    public override void Print()
    {
        Console.WriteLine("南昌的鸭脖");
    }
}

/// <summary>
/// 上海的鸭脖没有南昌的鸭脖做的辣
/// </summary>
public class ShangHaiYaBo : YaBo
{
    public override void Print()
    {
        Console.WriteLine("上海的鸭脖");
    }
}

/// <summary>
/// 南昌的鸭架
/// </summary>
public class NanChangYaJia : YaJia
{
    public override void Print()
    {
        Console.WriteLine("南昌的鸭架子");
    }
}

/// <summary>
/// 上海的鸭架
/// </summary>
public class ShangHaiYaJia : YaJia
{
    public override void Print()
    {
        Console.WriteLine("上海的鸭架子");
    }
}


/// <summary>
/// 如果绝味又想开一家湖南的分店时，因为湖南喜欢吃麻的
/// 所以这是有需要有一家湖南的工厂专门制作
/// </summary>
public class HuNanFactory : AbstractFactory
{
    // 制作湖南鸭脖
    public override YaBo CreateYaBo()
    {
        return new HuNanYaBo();
    }

    // 制作湖南鸭架
    public override YaJia CreateYaJia()
    {
        return new HuNanYajia();
    }
}

/// <summary>
/// 湖南的鸭脖
/// </summary>
public class HuNanYaBo : YaBo
{
    public override void Print()
    {
        Console.WriteLine("湖南的鸭脖");
    }
}

/// <summary>
/// 湖南的鸭架
/// </summary>
public class HuNanYajia : YaJia
{
    public override void Print()
    {
        Console.WriteLine("湖南的鸭架子");
    }
}

//此时，只需要添加三个类：一个是湖南具体工厂类，负责创建湖南口味的鸭脖和鸭架，另外两个类是具有湖南口味的鸭脖类和鸭架类。
//从上面代码看出，抽象工厂对于系列产品的变化支持 “开放——封闭”原则（指的是要求系统对扩展开放，对修改封闭），
//扩展起来非常简便，但是，抽象工厂对于添加新产品这种情况就不支持”开放——封闭 “原则，这也是抽象工厂的缺点所在，这点会在第四部分详细介绍。
#endregion 

#region 抽象工厂的分析
//抽象工厂模式将具体产品的创建延迟到具体工厂的子类中，这样将对象的创建封装起来，可以减少客户端与具体产品类之间的依赖，从而使系统耦合度低，
//这样更有利于后期的维护和扩展，这真是抽象工厂模式的优点所在，然后抽象模式同时也存在不足的地方。下面就具体看下抽象工厂的缺点：

//重点：抽象工厂模式很难支持新种类产品的变化。这是因为抽象工厂接口中已经确定了可以被创建的产品集合，如果需要添加新产品，此时就必须去修改抽象工厂的接口，
//这样就涉及到抽象工厂类的以及所有子类的改变，这样也就违背了“开发——封闭”原则。

//知道了抽象工厂的优缺点之后，也就能很好地把握什么情况下考虑使用抽象工厂模式了，下面就具体看看使用抽象工厂模式的系统应该符合那几个前提：
//1、一个系统不要求依赖产品类实例如何被创建、组合和表达的表达，这点也是所有工厂模式应用的前提。
//2、这个系统有多个系列产品，而系统中只消费其中某一系列产品
//3、系统要求提供一个产品类的库，所有产品以同样的接口出现，客户端不需要依赖具体实现。
#endregion